Process Analytics

import pandas as pd
df = pd.read_csv("../data/run_results.csv")

df.head(15)
    P_ID  run_number  ...       hems_result                    outcome
0      1           1  ...               NaN                        NaN
1      1           1  ...               NaN                        NaN
2      1           1  ...               NaN                        NaN
3      1           1  ...               NaN                        NaN
4      1           1  ...               NaN                        NaN
5      1           1  ...               NaN                        NaN
6      1           1  ...               NaN                        NaN
7      1           1  ...               NaN                        NaN
8      1           1  ...  Patient Conveyed                        NaN
9      1           1  ...  Patient Conveyed                        NaN
10     1           1  ...  Patient Conveyed  Conveyed by land with DAA
11     1           1  ...  Patient Conveyed  Conveyed by land with DAA
12     1           1  ...  Patient Conveyed  Conveyed by land with DAA
13     1           1  ...  Patient Conveyed  Conveyed by land with DAA
14     1           1  ...  Patient Conveyed  Conveyed by land with DAA

[15 rows x 22 columns]
df.columns
Index(['P_ID', 'run_number', 'time_type', 'event_type', 'timestamp',
       'timestamp_dt', 'day', 'hour', 'weekday', 'month', 'qtr',
       'registration', 'callsign', 'callsign_group', 'vehicle_type',
       'ampds_card', 'age', 'sex', 'care_cat', 'heli_benefit', 'hems_result',
       'outcome'],
      dtype='object')
df = df[df["event_type"]=="queue"]

df["activity_id"] = df.groupby("run_number").cumcount() + 1

# Duplicate rows and modify them
df_start = df.copy()

df_start["lifecycle_id"] = "start"

df_end = df.copy()
df_end["lifecycle_id"] = "complete"

# Shift timestamps for 'end' rows
df_end["timestamp"] = df_end["timestamp"].shift(-1)
df_end["timestamp_dt"] = df_end["timestamp_dt"].shift(-1)

# Combine and sort
df_combined = pd.concat([df_start, df_end]).sort_index(kind="stable")

# Drop last 'end' row (since there’s no next row to get a timestamp from)
df_combined = df_combined[:-1]

df_combined.to_csv("event_log.csv", index=False)

df_combined
        P_ID  run_number  ... activity_id lifecycle_id
10         1           1  ...           1        start
10         1           1  ...           1     complete
11         1           1  ...           2        start
11         1           1  ...           2     complete
12         1           1  ...           3        start
...      ...         ...  ...         ...          ...
452137  2693           9  ...       14835        start
452137  2693           9  ...       14835     complete
452138  2693           9  ...       14836        start
452138  2693           9  ...       14836     complete
452140  2693           9  ...       14837        start

[294851 rows x 24 columns]
library(readr)
library(bupaverse)
Warning: package 'bupaverse' was built under R version 4.3.3

.______    __    __  .______      ___   ____    ____  _______ .______          _______. _______
|   _  \  |  |  |  | |   _  \    /   \  \   \  /   / |   ____||   _  \        /       ||   ____|
|  |_)  | |  |  |  | |  |_)  |  /  ^  \  \   \/   /  |  |__   |  |_)  |      |   (----`|  |__
|   _  <  |  |  |  | |   ___/  /  /_\  \  \      /   |   __|  |      /        \   \    |   __|
|  |_)  | |  `--'  | |  |     /  _____  \  \    /    |  |____ |  |\  \----.----)   |   |  |____
|______/   \______/  | _|    /__/     \__\  \__/     |_______|| _| `._____|_______/    |_______|
                                                                                                
── Attaching packages ─────────────────────────────────────── bupaverse 0.1.0 ──
✔ bupaR         0.5.4     ✔ processcheckR 0.1.4
✔ edeaR         0.9.4     ✔ processmapR   0.5.6
✔ eventdataR    0.3.1     
Warning: package 'bupaR' was built under R version 4.3.3
Warning: package 'processcheckR' was built under R version 4.3.3
── Conflicts ────────────────────────────────────────── bupaverse_conflicts() ──
✖ bupaR::filter()          masks stats::filter()
✖ processmapR::frequency() masks stats::frequency()
✖ edeaR::setdiff()         masks base::setdiff()
✖ bupaR::timestamp()       masks utils::timestamp()
✖ processcheckR::xor()     masks base::xor()
library(processanimateR)
Warning: package 'processanimateR' was built under R version 4.3.3
data <- readr::read_csv("event_log.csv")
Rows: 294851 Columns: 24
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (14): time_type, event_type, day, weekday, registration, callsign, vehi...
dbl   (9): P_ID, run_number, timestamp, hour, month, qtr, callsign_group, ag...
dttm  (1): timestamp_dt

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
activity_log <- data %>%
    filter(run_number==1) %>%
    bupaR::convert_timestamps("timestamp_dt", ymd_hms) %>%
    bupaR::eventlog(
        case_id = "P_ID",
        activity_id = "time_type",
        activity_instance_id = "activity_id",
        lifecycle_id = "lifecycle_id",
        timestamp = "timestamp_dt",
        resource_id = "callsign"
        )
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `timestamp_dt = (function (..., quiet = FALSE, tz = "UTC",
  locale = Sys.getlocale("LC_TIME"), ...`.
Caused by warning:
!  72 failed to parse.
## !!!! Note that the bupaR documentation recommmends using the
## to_activitylog() function at the end of this set of steps.
## This caused significant errors in testing of this code

activity_log
# Log of 30576 events consisting of:
13 traces 
2727 cases 
15288 instances of 12 activities 
6 resources 
Events occurred from NA until NA 
 
# Variables were mapped as follows:
Case identifier:        P_ID 
Activity identifier:        time_type 
Resource identifier:        callsign 
Activity instance identifier:   activity_id 
Timestamp:          timestamp_dt 
Lifecycle transition:       lifecycle_id 

# A tibble: 30,576 × 25
    P_ID run_number time_type     event_type timestamp timestamp_dt        day  
   <dbl>      <dbl> <chr>         <chr>          <dbl> <dttm>              <chr>
 1     1          1 HEMS call st… queue              0 2025-04-03 08:00:00 Thu  
 2     1          1 HEMS call st… queue             12 2025-04-03 08:12:00 Thu  
 3     1          1 HEMS allocat… queue             12 2025-04-03 08:12:00 Thu  
 4     1          1 HEMS allocat… queue             15 2025-04-03 08:15:00 Thu  
 5     1          1 HEMS mobile   queue             15 2025-04-03 08:15:00 Thu  
 6     1          1 HEMS mobile   queue             26 2025-04-03 08:26:00 Thu  
 7     1          1 HEMS on scene queue             26 2025-04-03 08:26:00 Thu  
 8     1          1 HEMS on scene queue             46 2025-04-03 08:46:00 Thu  
 9     1          1 HEMS leaving… queue             46 2025-04-03 08:46:00 Thu  
10     1          1 HEMS leaving… queue            123 2025-04-03 10:03:00 Thu  
# ℹ 30,566 more rows
# ℹ 18 more variables: hour <dbl>, weekday <chr>, month <dbl>, qtr <dbl>,
#   registration <chr>, callsign <chr>, callsign_group <dbl>,
#   vehicle_type <chr>, ampds_card <chr>, age <dbl>, sex <chr>, care_cat <chr>,
#   heli_benefit <chr>, hems_result <chr>, outcome <chr>, activity_id <dbl>,
#   lifecycle_id <chr>, .order <int>
activity_log %>%
    process_map(frequency("absolute"))
Warning in process_map.eventlog(., frequency("absolute")): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!
activity_log %>%
    process_map(frequency("absolute-case"))
Warning in process_map.eventlog(., frequency("absolute-case")): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!
activity_log %>%
    process_map(frequency("relative"))
Warning in process_map.eventlog(., frequency("relative")): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!
activity_log %>%
    process_map(performance())
Warning in process_map.eventlog(., performance()): Some of the timestamps in
the supplied event log are missing (NA values). This may result in a invalid
process map!
activity_log %>%
    process_map(performance(FUN = max))
Warning in process_map.eventlog(., performance(FUN = max)): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!
Warning: There were 10 warnings in `summarize()`.
The first warning was:
ℹ In argument: `value = do.call(...)`.
ℹ In group 1: `ACTIVITY_CLASSIFIER_ = "ARTIFICIAL_END"`, `next_act =
  "ARTIFICIAL_START"`, `from_id = 1`, `to_id = 2`.
Caused by warning in `type()`:
! no non-missing arguments to max; returning -Inf
ℹ Run `dplyr::last_dplyr_warnings()` to see the 9 remaining warnings.
activity_log %>%
    trace_explorer(n_traces = 10)

activity_log %>%
    activity_presence() %>%
    plot()

activity_log %>%
    processing_time("resource-activity", units = "mins") %>%
    plot()
Warning: Removed 72 rows containing non-finite values (`stat_boxplot()`).

activity_log %>%
    processing_time("activity", units = "mins") %>%
    plot()
Warning: Removed 72 rows containing non-finite values (`stat_boxplot()`).

activity_log %>%
    idle_time("resource", units = "mins") %>%
    plot()

Animated Maps

activity_log %>%
    animate_process()
Warning in process_map.eventlog(eventlog, render = F, ...): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!
activity_log_2 <- data %>%
    filter(run_number==1) %>%
    bupaR::convert_timestamps("timestamp_dt", ymd_hms) %>%
    bupaR::eventlog(
        case_id = "P_ID",
        activity_id = "callsign",
        activity_instance_id = "activity_id",
        lifecycle_id = "lifecycle_id",
        timestamp = "timestamp_dt",
        resource_id = "callsign"
        )
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `timestamp_dt = (function (..., quiet = FALSE, tz = "UTC",
  locale = Sys.getlocale("LC_TIME"), ...`.
Caused by warning:
!  72 failed to parse.
activity_log_2 %>%
    animate_process()
Warning in process_map.eventlog(eventlog, render = F, ...): Some of the
timestamps in the supplied event log are missing (NA values). This may result
in a invalid process map!